#!/usr/bin/env python
# -*- coding: utf-8 -*-
from UserDict import UserDict

class odict(UserDict):
    """
    Словарь сохраняющий порядок
    """
    def __init__(self, dict = None):
        self._keys = []
        UserDict.__init__(self, dict)
    
    def __setitem__(self, key, item):
        UserDict.__setitem__(self, key, item)
        if key not in self._keys: self._keys.append(key)

    def copy(self):
        dict = UserDict.copy(self)
        dict._keys = self._keys[:]
        return dict

    def items(self):
        return zip(self._keys, self.values())
    
    def keys(self):
        return self._keys

    def update(self, dict):
        UserDict.update(self, dict)
        for key in dict.keys():
            if key not in self._keys: self._keys.append(key)
    
    def setdefault(self, key, failobj = None):
        UserDict.setdefault(self, key, failobj)
        if key not in self._keys: self._keys.append(key)

    def values(self):
        return map(self.get, self._keys)

class State(object):
    def __init__(self, key=-1):
        self.key = key
    
    def __repr__(self):
        return 'State - %s'%str(self.key)[:10]

class Storage:
    """
    Сгенерированная программа
    """
    def __init__(self):
        self.funs  = odict()
        self.stack = []
        self.lambda_cnt = 0

    def add(self, trace):
        self.funs[self.stack[-1]] = trace
    
    def can_cached(self, args):
        for arg in args:
            if type(arg) == State:
                if arg.key == '#next' or arg.key == '#curr':
                    return False
            elif arg == curr or arg == next:
                return False
        return True

    def call_fun(self, fn, *args):
        # автонумерация анонимов
        if getattr(fn,'__name__') == '<lambda>':
            setattr(fn,'__name__','#lambda-%s'%self.lambda_cnt)
            self.lambda_cnt += 1

        # закешированное значение
        key = '%s%s'%(getattr(fn,'__name__'),args)
        if not self.can_cached(args):
            key = '%s-%s'%(key, self.lambda_cnt)
            self.lambda_cnt += 1

        elif self.funs.has_key(key):
            return State(key)
        # выполняем
        def process(arg):
            if arg == curr:
                return State('#curr')
            elif arg == next:
                return State('#next')
            elif hasattr(arg,'__name__'):
                arg = self.call_fun(arg)                
            return arg

        self.stack.append(key)
        self.funs[key] = ['Flip','0', State('#next'), State('#next')]

        newargs = [process(arg) for arg in args]        
        fn(*newargs)
        self.stack.pop()
        
        return State(key)

    def output(self):
        result = []
        for key, item in self.funs.items():
            instr = []
            for elem in item:
                if type(elem) == State:
                    if elem.key == '#next':
                        pos = self.funs.keys().index(key)+1
                    elif elem.key == '#curr':
                        pos = self.funs.keys().index(key)
                    else:
                        pos = self.funs.keys().index(elem.key)
                    instr.append(str(pos))
                else:
                    instr.append(elem)
            result.append(' '.join(instr))
        return result

    def __repr__(self):
        result = []
        for key, item in self.funs.items():
            result.append(str(item))
        return '\n'.join(result)
        
Program = Storage()

def state(f):
    def wraper(*args):
        return Program.call_fun(f, *args)
    setattr(wraper, '__name__', getattr(f, '__name__'))
    return wraper

@state
def sense(direction, state_ok, state_else, condition):
    Program.add(['Sense', direction, state_ok, state_else, condition])

@state
def mark(marker, state):
    Program.add(['Mark', str(marker), state])

@state
def unmark(marker, state):
    Program.add(['Umark', str(marker), state])

@state
def pickup(state_ok, state_else):
    Program.add(['PickUp', state_ok, state_else])

@state
def drop(state):
    Program.add(['Drop', state])

@state
def turn(side, state):
    Program.add(['Turn', side, state])

@state
def move(state_ok, state_else):
    Program.add(['Move', state_ok, state_else])

@state
def flip(prob, state_ok, state_else):
    Program.add(['Flip', str(prob), state_ok, state_else])

def next():
    """
    Состояние - ссылка на следующую инструкцию
    """
    pass

def curr():
    """
    Состояние - ссылка на текущую инструкцию
    """
    pass

@state
def search():
    sense('Ahead', curr, lambda : move(curr, curr), 'Home')

